package com.jht.bletool2kotlin.service

import android.app.Service
import android.bluetooth.*
import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_INDICATE
import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import org.greenrobot.eventbus.EventBus
import java.util.*

private const val TAG = "BleService"


class BleService : Service() {
    @Volatile
    private var connectState: Int = STATE_START

    private val gattCallback = BleGattCallback()
    private var gatt: BluetoothGatt? = null
    private var currentBTDevice: BluetoothDevice? = null
    private val binder = BleBinder()

    @Volatile
    private var allCharacteristics: List<BluetoothGattCharacteristic> = mutableListOf()

    @Volatile
    private var map: Map<String, List<BluetoothGattCharacteristic>> = mutableMapOf()

    override fun onBind(intent: Intent): IBinder {
        return binder
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate: ")
        initState()
    }

    private fun initState() {
        connectState = STATE_START
    }

    override fun onDestroy() {
        super.onDestroy()
        disconnectBle()
        closeBleConn()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand: ")
        return super.onStartCommand(intent, flags, startId)
    }

    inner class BleBinder : Binder() {

        @Suppress("unused")
        fun getBleService(): BleService {
            return this@BleService
        }

    }

    @Suppress("unused")
    fun connectGatt(bluetoothDevice: BluetoothDevice) {
        currentBTDevice = bluetoothDevice
        gatt = bluetoothDevice.connectGatt(this, false, gattCallback)
        connectState = STATE_CONNECTING
    }

    @Suppress("unused")
    fun getCurrentBleAllCharacteristics(): List<BluetoothGattCharacteristic> {
        return allCharacteristics
    }

    @Suppress("unused")
    fun getCurrentBleAllAttributes(): Map<String, List<BluetoothGattCharacteristic>> {
        return map
    }

    fun sendBleStateEvent(state: Int, characteristic: BluetoothGattCharacteristic?) {
        when (state) {
            STATE_CONNECTED -> {
                connectState = STATE_CONNECTED
                sendBleStateEvent(state)
            }
            STATE_DISCONNECTED -> {
                closeBleConn()
                connectState = STATE_DISCONNECTED
                sendBleStateEvent(state)
            }
            STATE_DISCOVERY_SERVICE_FAIL -> {
                disconnectBle()
                sendBleStateEvent(state)
            }
            STATE_DISCOVERY_SERVICE_SUCCESS -> {
                sendBleStateEvent(state)
            }
            STATE_GATT_CALLBACK_CONNECT_FAIL -> {
                disconnectBle()
                closeBleConn()
                sendBleStateEvent(state)
            }
            STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FINISH -> {
                organizeBleServiceData()
                sendBleStateEvent(state)
            }
            STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FAIL -> {
                disconnectBle()
                sendBleStateEvent(state)
            }
            STATE_GATT_CALLBACK_READ_SUCCESS, STATE_GATT_CALLBACK_WRITE_SUCCESS, STATE_GATT_CALLBACK_NOTIFY_SUCCESS -> {
                if (characteristic != null) {
                    sendBleStateIncludeCharacterEvent(state, characteristic)
                }
            }
            STATE_GATT_CALLBACK_WRITE_FAIL, STATE_GATT_CALLBACK_READ_FAIL -> {
                if (characteristic != null) {
                    sendBleStateIncludeCharacterEvent(state, characteristic)
                }
            }
            STATE_GATT_CALLBACK_MTU_CHANGED_FAIL -> {
                sendBleStateEvent(state)
            }
        }
    }

    fun startDiscoverBleServices(): Boolean {
        return gatt?.discoverServices() ?: false
    }

    private fun closeBleConn() {
        connectState = STATE_DISCONNECTED
        this@BleService.gatt?.close()
    }

    private fun disconnectBle() {
        connectState = STATE_DISCONNECTING
        this@BleService.gatt?.disconnect()
    }

    /**
     * 整理ble服务数据
     */
    private fun organizeBleServiceData() {
        this@BleService.gatt?.services?.forEach { bluetoothGattService ->
            val character = bluetoothGattService.characteristics
            allCharacteristics.plus(character)
            map.plus(Pair(bluetoothGattService.uuid.toString(), character))
        }
    }

    fun sendBleMTUStateEvent(state: Int, mtu: Int) {
        val bleStateEvent = BleStateEvent(state)
        bleStateEvent.mtuSizeData = mtu
        EventBus.getDefault().post(bleStateEvent)
    }

    private fun sendBleStateEvent(state: Int) {
        val bleStateEvent = BleStateEvent(state)
        EventBus.getDefault().post(bleStateEvent)
    }

    private fun checkCharacteristicIsNull(characteristic: BluetoothGattCharacteristic?): Boolean {
        return characteristic == null
    }


    private fun sendBleStateIncludeCharacterEvent(
        state: Int,
        characteristic: BluetoothGattCharacteristic
    ) {
        if (checkCharacteristicIsNull(characteristic)) {
            EventBus.getDefault().post(BleErrorCode.CharacteristicNull)
        } else {
            val bleStateEvent = BleStateEvent(state)
            bleStateEvent.characteristicName = characteristic.uuid.toString()
            bleStateEvent.characteristicData = characteristic.value
            EventBus.getDefault().post(bleStateEvent)
        }

    }

    @Suppress("unused")
    fun readCharacteristicData(characteristic: BluetoothGattCharacteristic) {
        if (gatt?.readCharacteristic(characteristic) == true) {
            EventBus.getDefault().post(BleStateEvent(READ_BLE_DATA))
        } else {
            val readBleDataError = BleErrorCode.ReadBleDataError
            readBleDataError.description =
                "${readBleDataError.description}, and characteristic is ${characteristic.uuid}"
            EventBus.getDefault().post(readBleDataError)
        }
    }

    @Suppress("unused")
    fun writeCharacteristicData(characteristic: BluetoothGattCharacteristic, data: ByteArray) {
        characteristic.value = data
        if (gatt?.writeCharacteristic(characteristic) == true) {
            EventBus.getDefault().post(BleStateEvent(WRITE_BLE_DATA))
        } else {
            val writeBleDataError = BleErrorCode.WriteBleDataError
            writeBleDataError.description =
                "${writeBleDataError.description}, and characteristic is ${characteristic.uuid}"
            EventBus.getDefault().post(writeBleDataError)
        }
    }


    @Suppress("unused")
    fun enableNotificationOrIndication(
        characteristic: BluetoothGattCharacteristic,
        enable: Boolean
    ) {
        val characteristicProperties = getCharacteristicProperties(characteristic)
        when {
            characteristicProperties.contains(PROPERTY_NOTIFY) -> {
                if (enableNotify(characteristic, enable)) {
                    EventBus.getDefault().post(BleStateEvent(ENABLE_NOTIFY_OR_INDICATE))
                } else {
                    EventBus.getDefault().post(BleErrorCode.EnableNotifyOrIndicateError)
                }
            }
            characteristicProperties.contains(PROPERTY_INDICATE) -> {
                if (enableIndication(characteristic, enable)) {
                    EventBus.getDefault().post(BleStateEvent(ENABLE_NOTIFY_OR_INDICATE))
                } else {
                    EventBus.getDefault().post(BleErrorCode.EnableNotifyOrIndicateError)
                }
            }
            else -> {
                EventBus.getDefault().post(BleErrorCode.BleNotifyOrIndicateError)
            }
        }
    }

    private fun enableIndication(
        characteristic: BluetoothGattCharacteristic,
        enable: Boolean
    ): Boolean {
        val enableOk = gatt?.setCharacteristicNotification(characteristic, enable) ?: false
        if (enableOk) {
            val descriptor =
                characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
                    ?: return false
            if (enable) {
                descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
            } else {
                descriptor.value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
            }
            return gatt?.writeDescriptor(descriptor) ?: false
        }
        return false
    }

    private fun enableNotify(
        characteristic: BluetoothGattCharacteristic,
        enable: Boolean
    ): Boolean {
        val enableOk = gatt?.setCharacteristicNotification(characteristic, enable) ?: false
        if (enableOk) {
            val descriptor =
                characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
                    ?: return false
            if (enable) {
                descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            } else {
                descriptor.value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
            }
            return gatt?.writeDescriptor(descriptor) ?: false
        }
        return false
    }

    private fun getCharacteristicProperties(characteristic: BluetoothGattCharacteristic): List<Int> {
        val propertiesList = mutableListOf<Int>()
        val properties = characteristic.properties
        if (properties and PROPERTY_NOTIFY == PROPERTY_NOTIFY) {
            propertiesList.add(PROPERTY_NOTIFY)
        }
        if (properties and PROPERTY_INDICATE == PROPERTY_INDICATE) {
            propertiesList.add(PROPERTY_INDICATE)
        }
        return propertiesList
    }

    /**
     * 回调接口里面只发事件，不做任何数据处理和控制处理，这些全部交给BleService
     */
    inner class BleGattCallback : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                when (newState) {
                    BluetoothProfile.STATE_CONNECTED -> {
                        sendBleStateEvent(STATE_CONNECTED, null)
                        //发现ble服务
                        val isOK = startDiscoverBleServices()
                        if (!isOK) {
                            sendBleStateEvent(STATE_DISCOVERY_SERVICE_FAIL, null)
                        } else {
                            sendBleStateEvent(STATE_DISCOVERY_SERVICE_SUCCESS, null)
                        }
                    }
                    BluetoothProfile.STATE_DISCONNECTED -> {
                        sendBleStateEvent(STATE_DISCONNECTED, null)
                    }
                }
            } else {
                Log.w(TAG, "onConnectionStateChange: GATT_FAIL")
                sendBleStateEvent(STATE_GATT_CALLBACK_CONNECT_FAIL, null)
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                sendBleStateEvent(STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FINISH, null)
            } else {
                Log.w(TAG, "onServicesDiscovered: on Services Discovered fail")
                sendBleStateEvent(STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FAIL, null)
            }
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic?,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                sendBleStateEvent(STATE_GATT_CALLBACK_READ_SUCCESS, characteristic)
            } else {
                Log.w(TAG, "onCharacteristicRead: on Characteristic Read fail")
                sendBleStateEvent(STATE_GATT_CALLBACK_READ_FAIL, characteristic)
            }
        }

        override fun onCharacteristicWrite(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic?,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                sendBleStateEvent(STATE_GATT_CALLBACK_WRITE_SUCCESS, characteristic)
            } else {
                Log.w(TAG, "onCharacteristicWrite: on Characteristic Write fail")
                sendBleStateEvent(STATE_GATT_CALLBACK_WRITE_FAIL, characteristic)
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic?
        ) {
            sendBleStateEvent(STATE_GATT_CALLBACK_NOTIFY_SUCCESS, characteristic)
        }

        override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                sendBleMTUStateEvent(STATE_GATT_CALLBACK_MTU_CHANGED_SUCCESS, mtu)
            } else {
                Log.w(TAG, "onMtuChanged: Mtu Changed fail")
                sendBleStateEvent(STATE_GATT_CALLBACK_MTU_CHANGED_FAIL, null)
            }
        }

    }

    @Suppress("unused")
    class BleStateEvent(val state: Int) {
        lateinit var characteristicName: String
        lateinit var characteristicData: ByteArray
        var mtuSizeData: Int = 0
    }

    @Suppress("unused")
    //  @formatter:off
    companion object {

        //ble设备发送数据的类型
        const val ACTION_READ_DATA_OK  = "com.jht.bluetooth.le.ACTION_READ_DATA_OK"
        const val ACTION_WRITE_DATA_OK = "com.jht.bluetooth.le.ACTION_WRITE_DATA_OK"
        const val ACTION_NOTIFY_DATA   = "com.jht.bluetooth.le.ACTION_NOTIFY_DATA"
        const val ACTION_MTU_CHANGE    = "com.jht.bluetooth.le.ACTION_MTU_CHANGE"

        //用户连接ble设备时的种种状态
        const val STATE_START                     = 10
        const val STATE_CONNECTING                = 11
        const val STATE_CONNECTED                 = 12
        const val STATE_DISCONNECTED              = 13
        const val STATE_DISCONNECTING             = 14
        const val STATE_DISCOVERY_SERVICE_FAIL    = 15
        const val STATE_DISCOVERY_SERVICE_SUCCESS = 16

        //GATT回调接口返回数据是否OK的种种状态
        const val STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FINISH = 21
        const val STATE_GATT_CALLBACK_READ_SUCCESS             = 22
        const val STATE_GATT_CALLBACK_WRITE_SUCCESS            = 23
        const val STATE_GATT_CALLBACK_MTU_CHANGED_SUCCESS      = 24
        const val STATE_GATT_CALLBACK_NOTIFY_SUCCESS           = 25
        const val STATE_GATT_CALLBACK_CONNECT_FAIL             = 26
        const val STATE_GATT_CALLBACK_SERVICE_DISCOVERY_FAIL   = 27
        const val STATE_GATT_CALLBACK_READ_FAIL                = 28
        const val STATE_GATT_CALLBACK_WRITE_FAIL               = 29
        const val STATE_GATT_CALLBACK_MTU_CHANGED_FAIL         = 30

        //用户对蓝牙的各种操作
        const val ENABLE_NOTIFY_OR_INDICATE = 40
        const val READ_BLE_DATA = 41
        const val WRITE_BLE_DATA = 42
    }
    //  @formatter:on

    @Suppress("unused")
    enum class BluetoothServiceUUID(val serviceName: String, private val uuid: String) {
        FTMS("FITNESS MACHINE SERVICE", "00001826-0000-1000-8000-00805F9B34FB");

        fun findAllCharacteristic(): List<BluetoothCharacteristicUUID> {
            val characters = BluetoothCharacteristicUUID.values()
            return characters.filter {
                it.service.uuid == uuid
            }
        }

    }

    @Suppress("unused")
    enum class BluetoothCharacteristicUUID(
        val service: BluetoothServiceUUID,
        val characteristicName: String,
        val uuid: String
    ) {
        FITNESS_MACHINE_FEATURE(
            BluetoothServiceUUID.FTMS,
            "FITNESS MACHINE FEATURE",
            "00002acc-0000-1000-8000-00805F9B34FB"
        ),
        INDOOR_BIKE_DATA(
            BluetoothServiceUUID.FTMS,
            "INDOOR BIKE DATA",
            "00002ad2-0000-1000-8000-00805F9B34FB"
        ),

    }

}
